home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / Pascal / Libraries / WASTE 1.1a4 / WASTE Source / WELowLevelEditing.p < prev    next >
Encoding:
Text File  |  1994-10-30  |  27.4 KB  |  990 lines  |  [TEXT/PJMM]

  1. unit WELowLevelEditing;
  2.  
  3. { WASTE PROJECT: }
  4. { Low-Level Editing Routines }
  5.  
  6. { Copyright © 1993-1994 Marco Piovanelli }
  7. { All Rights Reserved }
  8.  
  9. interface
  10.     uses
  11.         WESelecting;
  12.  
  13. { routines for implementing intelligent cut-and-paste rules }
  14.  
  15.     function _WEIsWordRange (rangeStart, rangeEnd: LongInt;
  16.                                     hWE: WEHandle): Boolean;
  17.     procedure _WEIntelligentCut (var rangeStart, rangeEnd: LongInt;
  18.                                     hWE: WEHandle);
  19.     function _WEIntelligentPaste (rangeStart, rangeEnd: LongInt;
  20.                                     hWE: WEHandle): Integer;
  21.  
  22. { low-level editing routines }
  23.  
  24.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  25.                                     hWE: WEHandle): OSErr;
  26.     function _WEInsertText (offset: LongInt;
  27.                                     textPtr: Ptr;
  28.                                     textLength: LongInt;
  29.                                     hWE: WEHandle): OSErr;
  30.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  31.                                     hStyles: StScrpHandle;
  32.                                     hWE: WEHandle): OSErr;
  33.     function _WEApplySoup (offset: LongInt;
  34.                                     hSoup: Handle;
  35.                                     hWE: WEHandle): OSErr;
  36.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  37.                                     mode: Integer;
  38.                                     var ts: WETextStyle;
  39.                                     hWE: WEHandle): OSErr;
  40.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  41.                                     hWE: WEHandle): OSErr;
  42.  
  43. implementation
  44.  
  45.     function _WEIsWordRange (rangeStart, rangeEnd: LongInt;
  46.                                     hWE: WEHandle): Boolean;
  47.  
  48. { _WEIsWordRange returns TRUE if the specified range is a word range, }
  49. { i.e. if it would be possible to select it by double-clicking and (optionally) dragging. }
  50.  
  51.         var
  52.             wordStart, wordEnd: LongInt;
  53.     begin
  54.         _WEIsWordRange := false;
  55.  
  56. { determine if rangeStart is at the beginning of a word }
  57.         WEFindWord(rangeStart, kLeadingEdge, wordStart, wordEnd, hWE);
  58.         if (rangeStart = wordStart) then
  59.             begin
  60.  
  61. { determine if rangeEnd is at the end of a word }
  62.                 WEFindWord(rangeEnd, kTrailingEdge, wordStart, wordEnd, hWE);
  63.                 _WEIsWordRange := (rangeEnd = wordEnd);
  64.             end;
  65.     end;  { _WEIsWordRange }
  66.  
  67.     function _WEIsPunct (offset: LongInt;
  68.                                     hWE: WEHandle): Boolean;
  69.         var
  70.             cType: Integer;
  71.     begin
  72.         _WEIsPunct := false;
  73.         cType := WECharType(offset, hWE);
  74.         if (BitAnd(cType, smcTypeMask) = smCharPunct) then
  75.             begin
  76.                 cType := BitAnd(cType, smcClassMask);
  77.                 if (cType = smPunctNormal) or (cType = smPunctBlank) then
  78.                     _WEIsPunct := true;
  79.             end;
  80.     end;  { _WEIsPunct }
  81.  
  82.     procedure _WEIntelligentCut (var rangeStart, rangeEnd: LongInt;
  83.                                     hWE: WEHandle);
  84.  
  85. { _WEIntelligentCut is called by other WASTE routines to determine the actual }
  86. { range to be deleted when weFIntCutAndPaste is enabled. }
  87. { On entry, rangeStart and rangeEnd specify the selection range visible to the user. }
  88. { On exit, rangeStart and rangeEnd specify the actual range to be removed. }
  89.  
  90.     begin
  91.  
  92. { do nothing if the intelligent cut-and-paste feature is disabled }
  93.         if (not BTST(hWE^^.flags, weFIntCutAndPaste)) then
  94.             Exit(_WEIntelligentCut);
  95.  
  96. { intelling cut-&-paste rules should be applied only to word ranges... }
  97.         if (_WEIsWordRange(rangeStart, rangeEnd, hWE) = false) then
  98.             Exit(_WEIntelligentCut);
  99.  
  100. { ...without punctuation characters at the beginning or end }
  101.         if (_WEIsPunct(rangeStart, hWE)) then
  102.             Exit(_WEIntelligentCut);
  103.         if (_WEIsPunct(rangeEnd - 1, hWE)) then
  104.             Exit(_WEIntelligentCut);
  105.  
  106. { if the character preceding the selection range is a space, discard it }
  107.         if (WEGetChar(rangeStart - 1, hWE) = CHR(32)) then
  108.             rangeStart := rangeStart - 1
  109.  
  110. { else, if the character following the selection range is a space, discard it }
  111.         else if (WEGetChar(rangeEnd, hWE) = CHR(32)) then
  112.             rangeEnd := rangeEnd + 1;
  113.  
  114.     end;  { _WEIntelligentCut }
  115.  
  116.     function _WEIntelligentPaste (rangeStart, rangeEnd: LongInt;
  117.                                     hWE: WEHandle): Integer;
  118.  
  119. { _WEIntelligentPaste is called by other WASTE routines to determine whether }
  120. { an additional space character should be added (before or after) after inserting }
  121. { new text (usually from the Clipboard or from a drag). }
  122.  
  123.     begin
  124.         _WEIntelligentPaste := weDontAddSpaces;
  125.  
  126. { do nothing unless the intelligent cut-and-paste feature is enabled }
  127.         if (not BTST(hWE^^.flags, weFIntCutAndPaste)) then
  128.             Exit(_WEIntelligentPaste);
  129.  
  130. { extra spaces will be added only if the pasted text looks like a word range, }
  131. { without punctuation characters at the beginning or at the end }
  132.         if _WEIsPunct(rangeStart, hWE) then
  133.             Exit(_WEIntelligentPaste);
  134.         if _WEIsPunct(rangeEnd - 1, hWE) then
  135.             Exit(_WEIntelligentPaste);
  136.  
  137. { if the character on the left of the pasted text is a punctuation character }
  138. { and the character on the right isn't, then add a space on the right, and vice versa }
  139.         if _WEIsPunct(rangeStart - 1, hWE) then
  140.             begin
  141.                 if (_WEIsPunct(rangeEnd, hWE) = false) then
  142.                     _WEIntelligentPaste := weAddSpaceOnRightSide;
  143.             end
  144.         else if _WEIsPunct(rangeEnd, hWE) then
  145.             _WEIntelligentPaste := weAddSpaceOnLeftSide;
  146.  
  147.     end;  { _WEIntelligentPaste }
  148.  
  149.     function _WEInsertRun (runIndex: LongInt;
  150.                                     offset, styleIndex: LongInt;
  151.                                     pWE: WEPtr): OSErr;
  152.  
  153. { Insert a new element in the style run array, at the specified runIndex position. }
  154. { The new element consists of the pair <offset, styleIndex>. }
  155.  
  156.         var
  157.             element: RunArrayElement;
  158.             err: OSErr;
  159.     begin
  160.         _WEInsertRun := noErr;
  161.  
  162. { prepare the element record to be inserted in the array }
  163.         element.runStart := offset;
  164.         element.styleIndex := styleIndex;
  165.  
  166. { do the insertion }
  167.         err := _WEInsertSlot(pWE^.hRuns, @element, runIndex + 1, SizeOf(element));
  168.         if (err <> noErr) then
  169.             begin
  170.                 _WEInsertRun := err;
  171.                 Exit(_WEInsertRun);
  172.             end;
  173.  
  174. { increment style run count }
  175.         pWE^.nRuns := pWE^.nRuns + 1;
  176.  
  177. { increment the reference count field of the style table element }
  178. { referenced by the newly inserted style run }
  179.         with pWE^.hStyles^^[styleIndex] do
  180.             refCount := refCount + 1;
  181.  
  182.     end;  { _WEInsertRun }
  183.  
  184.     function _WERemoveRun (runIndex: LongInt;
  185.                                     pWE: WEPtr): OSErr;
  186.  
  187. { remove the specified element from the style run array }
  188.  
  189.         var
  190.             styleIndex: LongInt;
  191.     begin
  192.  
  193.         styleIndex := pWE^.hRuns^^[runIndex].styleIndex;
  194.  
  195. { do the removal (errors returned by _WERemoveSlot can be safely ignored) }
  196.         _WERemoveRun := _WERemoveSlot(pWE^.hRuns, runIndex, SizeOf(RunArrayElement));
  197.  
  198. { decrement style run count }
  199.         pWE^.nRuns := pWE^.nRuns - 1;
  200.  
  201. { decrement the reference count field of the style table element }
  202. { that was referenced by the style run we have just removed }
  203.         with pWE^.hStyles^^[styleIndex] do
  204.             begin
  205.                 refCount := refCount - 1;
  206.  
  207. { dispose of embedded object, if any }
  208.                 if (refCount = 0) then
  209.                     if (_WEFreeObject(WEObjectDescHandle(info.runStyle.tsObject)) <> noErr) then
  210.                         ;
  211.             end;
  212.  
  213.     end;  { _WERemoveRun }
  214.  
  215.     procedure _WEChangeRun (runIndex: LongInt;
  216.                                     newStyleIndex: LongInt;
  217.                                     keepOld: Boolean;
  218.                                     pWE: WEPtr);
  219.  
  220. { change the styleIndex field of the specified element of the style run array }
  221.  
  222.         var
  223.             oldStyleIndex: LongInt;
  224.             hObjectDesc: WEObjectDescHandle;
  225.     begin
  226.  
  227. { do the change }
  228.         with pWE^.hRuns^^[runIndex] do
  229.             begin
  230.                 oldStyleIndex := styleIndex;
  231.                 styleIndex := newStyleIndex;
  232.             end;
  233.  
  234. { increment the reference count field of the new style table element }
  235.         with pWE^.hStyles^^[newStyleIndex] do
  236.             begin
  237.                 refCount := refCount + 1;
  238.                 hObjectDesc := WEObjectDescHandle(info.runStyle.tsObject);
  239.             end;
  240.  
  241. { decrement the reference count field of the old style table element }
  242.         with pWE^.hStyles^^[oldStyleIndex] do
  243.             begin
  244.                 refCount := refCount - 1;
  245.  
  246. { dispose of embedded object, if any, unless it is again referenced in the new style }
  247.                 if ((refCount = 0) and (keepOld = false)) then
  248.                     if (WEObjectDescHandle(info.runStyle.tsObject) <> hObjectDesc) then
  249.                         if (_WEFreeObject(WEObjectDescHandle(info.runStyle.tsObject)) <> noErr) then
  250.                             ;
  251.  
  252.             end;
  253.     end;  { _WEChangeRun }
  254.  
  255.     function _WENewStyle (var ts: WERunAttributes;
  256.                                     var styleIndex: LongInt;
  257.                                     pWE: WEPtr): OSErr;
  258.  
  259. { given the specified WERunAttributes record, find the corresponding entry }
  260. { in the style table (create a new entry if necessary), and return its index }
  261.  
  262.         var
  263.             pTable: StyleTablePtr;
  264.             element: StyleTableElement;
  265.             index, unusedIndex: LongInt;
  266.             err: OSErr;
  267.     begin
  268.         _WENewStyle := noErr;
  269.         pTable := pWE^.hStyles^;
  270.  
  271. { see if the given style already exists in the style table }
  272. { while scanning the table, also remember the position of the first unused style, if any }
  273.         index := 0;
  274.         unusedIndex := -1;
  275.         while (index < pWE^.nStyles) do
  276.             begin
  277.  
  278. { perform a bitwise comparison between the current element and the specified style }
  279.                 if _WEBlockCmp(@pTable^[index].info, @ts, SizeOf(ts)) then
  280.                     begin
  281.                         styleIndex := index;        { found: style already present }
  282.                         Exit(_WENewStyle);
  283.                     end;
  284.  
  285. { check for entries which aren't referenced and can be recycled }
  286.                 if (pTable^[index].refCount = 0) then
  287.                     unusedIndex := index;
  288.  
  289.                 index := index + 1;
  290.             end;  { while }
  291.  
  292. { the specified style doesn't exist in the style table }
  293. { see if we can recycle an unused entry }
  294.         if (unusedIndex >= 0) then
  295.             begin
  296.                 index := unusedIndex;
  297.                 pTable^[index].info := ts;
  298.             end
  299.         else
  300.             begin
  301.  
  302. { no reusable entry: we have to append a new element to the table }
  303.                 element.refCount := 0;
  304.                 element.info := ts;
  305.                 err := _WEInsertSlot(pWE^.hStyles, @element, index, SizeOf(element));
  306.                 if (err <> noErr) then
  307.                     begin
  308.                         _WENewStyle := err;
  309.                         Exit(_WENewStyle);
  310.                     end;
  311.  
  312. { update style count in the WE record }
  313.                 pWE^.nStyles := index + 1;
  314.  
  315.             end;
  316.  
  317. { return the index to the new element }
  318.         styleIndex := index;
  319.  
  320.     end;  { _WENewStyle }
  321.  
  322.     function _WERedraw (rangeStart, rangeEnd: LongInt;
  323.                                     hWE: WEHandle): OSErr;
  324.  
  325. { the WE record is guaranteed to be already locked }
  326.  
  327.         label
  328.             1;
  329.         var
  330.             pWE: WEPtr;
  331.             pLines: LineArrayPtr;
  332.             startLine, endLine: LongInt;
  333.             oldTextHeight, newTextHeight: LongInt;
  334.             r: LongRect;
  335.             viewRect, updateRect: Rect;
  336.             saveClip: RgnHandle;
  337.             savePort: GrafPtr;
  338.             err: OSErr;
  339.     begin
  340.         pWE := hWE^;
  341.  
  342. { do nothing if recalculation has been inhibited }
  343.         if (not BTST(pWE^.flags, weFInhibitRecal)) then
  344.             begin
  345.  
  346. { hide the caret }
  347.                 if BTST(pWE^.flags, weFCaretVisible) then
  348.                     _WEBlinkCaret(hWE);
  349.  
  350. { remember total text height }
  351.                 oldTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  352.  
  353. { find line range affected by modification }
  354.                 startLine := WEOffsetToLine(rangeStart, hWE);
  355.                 endLine := WEOffsetToLine(rangeEnd, hWE);
  356.  
  357. { recalculate line breaks starting from startLine }
  358.                 err := _WERecalBreaks(startLine, endLine, hWE);
  359.                 if (err <> noErr) then
  360.                     goto 1;
  361.  
  362. { recalculate slops }
  363.                 _WERecalSlops(startLine, endLine, hWE);
  364.  
  365. { calculate new total text height }
  366.                 newTextHeight := pWE^.destRect.bottom - pWE^.destRect.top;
  367.  
  368. { calculate the rectangle to redraw (in long coordinates) }
  369.                 r.left := -maxint;
  370.                 r.right := maxint;
  371.                 pLines := pWE^.hLines^;
  372.                 r.top := pLines^[startLine].lineOrigin;
  373.  
  374. { if total text height hasn't changed, it's enough to redraw lines up to endLine }
  375. { otherwise we must redraw all lines from startLine on }
  376.                 if ((newTextHeight = oldTextHeight) and (endLine < pWE^.nLines - 1)) then
  377.                     r.bottom := pLines^[endLine + 1].lineOrigin
  378.                 else if (newTextHeight < oldTextHeight) then
  379.                     r.bottom := oldTextHeight
  380.                 else
  381.                     r.bottom := newTextHeight;
  382.  
  383.                 WEOffsetLongRect(r, 0, pWE^.destRect.top);
  384.  
  385. { calculate the intersection between this rectangle and the view rectangle }
  386.                 WELongRectToRect(r, updateRect);
  387.                 WELongRectToRect(pWE^.viewRect, viewRect);
  388.  
  389.                 if SectRect(updateRect, viewRect, updateRect) then
  390.                     begin
  391.  
  392. { set up the port and the clip region }
  393.                         GetPort(savePort);
  394.                         SetPort(pWE^.port);
  395.  
  396. { set the clip region to updateRect }
  397.                         saveClip := NewRgn;
  398.                         GetClip(saveClip);
  399.                         ClipRect(updateRect);
  400.  
  401. { we only really need to redraw the visible lines }
  402.                         startLine := _WEPixelToLine(updateRect.top - pWE^.destRect.top, hWE);
  403.                         endLine := _WEPixelToLine(updateRect.bottom - pWE^.destRect.top - 1, hWE);
  404.  
  405. { redraw the lines (pass TRUE in the doErase parameter) }
  406.                         _WEDrawLines(startLine, endLine, true, hWE);
  407.  
  408. { erase the portion of the update rectangle below the last line (if any) }
  409.                         pLines := pWE^.hLines^;
  410.                         updateRect.top := pWE^.destRect.top + pLines^[endLine + 1].lineOrigin;
  411.                         if (updateRect.top < updateRect.bottom) then
  412.                             EraseRect(updateRect);
  413.  
  414. { restore the clip region }
  415.                         SetClip(saveClip);
  416.                         DisposeRgn(saveClip);
  417.  
  418. { restore the port }
  419.                         SetPort(savePort);
  420.  
  421. { redraw the caret or the selection range }
  422.                         if (pWE^.selStart < pWE^.selEnd) then
  423.                             _WEHiliteRange(pWE^.selStart, pWE^.selEnd, hWE)
  424.                         else
  425.                             _WEBlinkCaret(hWE);
  426.  
  427.                     end;  { if SectRect }
  428.  
  429. { scroll the selection range into view }
  430.                 WESelView(hWE);
  431.  
  432.             end;  { if recal not inhibited }
  433.  
  434. { clear result code }
  435.         err := noErr;
  436.  
  437. 1:
  438. { return result code }
  439.         _WERedraw := err;
  440.  
  441.     end;  { _WERedraw }
  442.  
  443.     function _WESetStyleRange (rangeStart, rangeEnd: LongInt;
  444.                                     mode: Integer;
  445.                                     var ts: WETextStyle;
  446.                                     hWE: WEHandle): OSErr;
  447.  
  448. { alter the style attributes of the specified text range according to ts and mode }
  449. { the WE record is guaranteed to be already locked }
  450.  
  451.         label
  452.             1;
  453.         var
  454.             pWE: WEPtr;
  455.             hRuns: RunArrayHandle;
  456.             offset: LongInt;
  457.             runIndex: LongInt;
  458.             oldStyleIndex, newStyleIndex: LongInt;
  459.             runInfo: WERunInfo;
  460.             temp: Integer;
  461.             continuousStyles: SignedByte;
  462.             err: OSErr;
  463.     begin
  464.         pWE := hWE^;
  465.         hRuns := pWE^.hRuns;
  466.  
  467. { if mode contains weDoToggleFace, we need to determine which QuickDraw styles }
  468. { are continuous over the specified text range: those styles must be turned off }
  469.         if BTST(mode, kModeToggleFace) then
  470.             begin
  471.                 temp := weDoFace;
  472.                 _WEContinuousStyleRange(rangeStart, rangeEnd, temp, runInfo.runAttrs.runStyle, hWE);
  473.                 continuousStyles := runInfo.runAttrs.runStyle.tsFace;
  474.             end
  475.         else
  476.             continuousStyles := 0;
  477.  
  478. { find the index to the first style run in the specified range }
  479.         offset := rangeStart;
  480.         runIndex := _WEOffsetToRun(offset, hWE);
  481.  
  482. { run thru all the style runs that encompass the selection range }
  483.         repeat
  484.  
  485. { find style index for this run and retrieve corresponding style attributes }
  486.             oldStyleIndex := hRuns^^[runIndex].styleIndex;
  487.             _WEGetIndStyle(runIndex, runInfo, hWE);
  488.  
  489. { _WEGetIndStyle returns textLength + 1 in runInfo.runEnd for the last style run: }
  490. { correct this anomaly (which is useful for other purposes, anyway) }
  491.             if (runInfo.runEnd > pWE^.textLength) then
  492.                 runInfo.runEnd := pWE^.textLength;
  493.  
  494. { apply changes to existing style attributes as requested }
  495.             _WECopyStyle(ts, runInfo.runAttrs.runStyle, continuousStyles, mode);
  496.  
  497. { recalculate font metrics, if necessary }
  498.             if (BitAnd(mode, weDoFont + weDoSize + weDoFace + weDoAddSize) <> 0) then
  499.                 _WEFillFontInfo(pWE^.port, runInfo.runAttrs);
  500.  
  501. { get a style index for the new attributes }
  502.             err := _WENewStyle(runInfo.runAttrs, newStyleIndex, pWE);
  503.             if (err <> noErr) then
  504.                 goto 1;
  505.  
  506. { if offset falls on a style boundary and this style run has become identical }
  507. { to the previous one, merge the two runs together }
  508.             if (offset = runInfo.runStart) & (runIndex > 0) & (hRuns^^[runIndex - 1].styleIndex = newStyleIndex) then
  509.                 begin
  510.                     err := _WERemoveRun(runIndex, pWE);
  511.                     if (err <> noErr) then
  512.                         goto 1;
  513.                     runIndex := runIndex - 1;
  514.                 end;
  515.  
  516. { style index changed? }
  517.             if (oldStyleIndex <> newStyleIndex) then
  518.                 begin
  519.  
  520. { if offset is in the middle of a style run, insert a new style run in the run array }
  521.                     if (offset > runInfo.runStart) then
  522.                         begin
  523.                             err := _WEInsertRun(runIndex, offset, newStyleIndex, pWE);
  524.                             if (err <> noErr) then
  525.                                 goto 1;
  526.                             runIndex := runIndex + 1;
  527.                         end
  528.                     else
  529.  
  530. { otherwise just change the styleIndex field of the current style run element }
  531.                         _WEChangeRun(runIndex, newStyleIndex, rangeEnd < runInfo.runEnd, pWE);
  532.  
  533. { if specified range ends in the middle of a style run, insert yet another element }
  534.                     if (rangeEnd < runInfo.runEnd) then
  535.                         begin
  536.                             err := _WEInsertRun(runIndex, rangeEnd, oldStyleIndex, pWE);
  537.                             if (err <> noErr) then
  538.                                 goto 1;
  539.                         end;
  540.  
  541.                 end;  { if oldStyle <> newStyle }
  542.  
  543. { go to next style run }
  544.             runIndex := runIndex + 1;
  545.             offset := runInfo.runEnd;
  546.  
  547.         until (offset >= rangeEnd);
  548.  
  549. { if the last style run ends exactly at the end of the specified range, }
  550. { see if we can merge it with the following style run }
  551.         if ((offset = rangeEnd) & (runIndex < pWE^.nRuns) & (hRuns^^[runIndex].styleIndex = newStyleIndex)) then
  552.             begin
  553.                 err := _WERemoveRun(runIndex, pWE);
  554.                 if (err <> noErr) then
  555.                     goto 1;
  556.             end;
  557.  
  558. { clear result code }
  559.         err := noErr;
  560.  
  561. 1:
  562. { return result code }
  563.         _WESetStyleRange := err;
  564.  
  565.     end;  { _WESetStyleRange }
  566.  
  567.     function _WEApplyStyleScrap (rangeStart, rangeEnd: LongInt;
  568.                                     hStyles: StScrpHandle;
  569.                                     hWE: WEHandle): OSErr;
  570.  
  571. { apply the given style scrap to the specified text range }
  572. { the WE record is guaranteed to be already locked }
  573.  
  574.         var
  575.             pWE: WEPtr;
  576.             pElement: TEStyleScrapPeek;
  577.             runStart, runEnd: LongInt;
  578.             index, lastElement: Integer;
  579.             ts: WETextStyle;
  580.             err: OSErr;
  581.     begin
  582.         _WEApplyStyleScrap := noErr;
  583.         pWE := hWE^;
  584.  
  585. { loop through each element of the style scrap }
  586.         lastElement := hStyles^^.scrpNStyles - 1;
  587.         for index := 0 to lastElement do
  588.             begin
  589.  
  590. { get a pointer to the current scrap element }
  591.                 pElement := @hStyles^^.scrpStyleTab[index];
  592.  
  593. { calculate text run to which this element is to be applied }
  594.                 runStart := rangeStart + pElement^.first.scrpStartChar;
  595.                 if (index < lastElement) then
  596.                     runEnd := rangeStart + pElement^.second.scrpStartChar
  597.                 else
  598.                     runEnd := rangeEnd;
  599.  
  600. { perform some range checking }
  601.                 if (runEnd > rangeEnd) then
  602.                     runEnd := rangeEnd;
  603.                 if (runStart >= runEnd) then
  604.                     Cycle;
  605.  
  606. { copy TextEdit style to a local variable in case memory moves }
  607.                 ts.tsTEStyle := pElement^.first.scrpTEAttrs.runTEStyle;
  608.  
  609. { apply the specified style to the range }
  610.                 err := _WESetStyleRange(runStart, runEnd, weDoAll + weDoReplaceFace, ts, hWE);
  611.                 if (err <> noErr) then
  612.                     begin
  613.                         _WEApplyStyleScrap := err;
  614.                         Exit(_WEApplyStyleScrap);
  615.                     end;
  616.  
  617.             end;  { for }
  618.     end;  { _WEApplyStyleScrap }
  619.  
  620.     function _WEApplySoup (offset: LongInt;
  621.                                     hSoup: Handle;
  622.                                     hWE: WEHandle): OSErr;
  623.         label
  624.             1;
  625.         var
  626.             pSoup: WESoupPtr;
  627.             pSoupEnd: LongInt;
  628.             ts: WETextStyle;
  629.             hObjectData: Handle;
  630.             objectOffset: LongInt;
  631.             saveWELock: Boolean;
  632.             err: OSErr;
  633.     begin
  634.         _WEBlockClr(@ts, SizeOf(ts));
  635.         hObjectData := nil;
  636.  
  637. { lock the WE record }
  638.         saveWELock := _WESetHandleLock(hWE, true);
  639.  
  640. { lock the soup in high heap }
  641.         HLockHi(hSoup);
  642.         pSoup := WESoupPtr(hSoup^);
  643.         pSoupEnd := LongInt(pSoup) + %_GetHandleSize(hSoup);
  644.  
  645. { loop through each object descriptor in the soup }
  646.         while (LongInt(pSoup) < pSoupEnd) do
  647.             begin
  648.  
  649. { create a new relocatable block the hold the object data }
  650.                 err := _WEAllocate(pSoup^.soupDataSize, kAllocTemp, hObjectData);
  651.                 if (err <> noErr) then
  652.                     goto 1;
  653.  
  654. { copy the object data to this block }
  655.                 %_BlockMoveData(Ptr(LongInt(pSoup) + SizeOf(WESoup)), hObjectData^, pSoup^.soupDataSize);
  656.  
  657. { create a new object out of the tagged data }
  658.                 err := _WENewObject(pSoup^.soupType, hObjectData, hWE, WEObjectDescHandle(ts.tsObject));
  659.                 if (err <> noErr) then
  660.                     goto 1;
  661.  
  662. { record a reference to the object descriptor in the style table }
  663.                 objectOffset := pSoup^.soupOffset + offset;
  664.                 err := _WESetStyleRange(objectOffset, objectOffset + 1, weDoObject, ts, hWE);
  665.                 hObjectData := nil;
  666.                 ts.tsObject := kNullObject;
  667.                 if (err <> noErr) then
  668.                     goto 1;
  669.  
  670. { advance soup pointer }
  671.                 pSoup := WESoupPtr(LongInt(pSoup) + SizeOf(WESoup) + pSoup^.soupDataSize);
  672.  
  673.             end;  { while }
  674.  
  675. { clear result code }
  676.         err := noErr;
  677.  
  678. 1:
  679. { return result code }
  680.         _WEApplySoup := err;
  681.  
  682. { clean up }
  683.         HUnlock(hSoup);
  684.         _WEForgetHandle(ts.tsObject);
  685.         _WEForgetHandle(hObjectData);
  686.  
  687. { unlock the WE record }
  688.         IgnoreBoolean(_WESetHandleLock(hWE, saveWELock));
  689.  
  690.     end;  { _WEApplySoup }
  691.  
  692.     procedure _WEBumpRunStart (runIndex: LongInt;
  693.                                     deltaRunStart: LongInt;
  694.                                     pWE: WEPtr);
  695.  
  696. { add deltaLineStart to the lineStart field of all line records }
  697. { starting from lineIndex }
  698.  
  699.         var
  700.             pStart: LongIntPtr;
  701.             nRuns: LongInt;
  702.     begin
  703.         pStart := @pWE^.hRuns^^[runIndex].runStart;
  704.         nRuns := pWE^.nRuns;
  705.  
  706. { loop through the style run array adjusting the runStart fields }
  707.         while (runIndex <= nRuns) do
  708.             begin
  709.                 pStart^ := pStart^ + deltaRunStart;
  710.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(RunArrayElement));
  711.                 runIndex := runIndex + 1;
  712.             end;
  713.     end;  { _WEBumpRunStart }
  714.  
  715.     function _WERemoveRunRange (rangeStart, rangeEnd: LongInt;
  716.                                     hWE: WEHandle): OSErr;
  717.  
  718. { the range of text between rangeStart and rangeEnd is being deleted }
  719. { update the style run array (and the style table) accordingly }
  720. { the WE handle must be locked on entry }
  721.  
  722.         label
  723.             1;
  724.         var
  725.             pWE: WEPtr;
  726.             pRuns: RunArrayPeek;
  727.             startRun, endRun: LongInt;
  728.             err: OSErr;
  729.     begin
  730.         pWE := hWE^;
  731.  
  732. { find the index to the first and last style runs in the specified range }
  733.         startRun := _WEOffsetToRun(rangeStart, hWE);
  734.         endRun := _WEOffsetToRun(rangeEnd, hWE) - 1;
  735.  
  736. { remove all style runs between startRun and endRun }
  737.         while (endRun > startRun) do
  738.             begin
  739.                 err := _WERemoveRun(endRun, pWE);
  740.                 if (err <> noErr) then
  741.                     goto 1;
  742.                 endRun := endRun - 1;
  743.             end;
  744.  
  745. { move back all subsequent style runs }
  746.         _WEBumpRunStart(startRun + 1, rangeStart - rangeEnd, pWE);
  747.  
  748.         if (endRun = startRun) and (endRun < pWE^.nRuns - 1) then
  749.             begin
  750.                 pRuns := @pWE^.hRuns^^[endRun];
  751.                 pRuns^.second.runStart := rangeStart;
  752.             end;
  753.  
  754. { remove the first style run if is has become zero length }
  755.         pRuns := @pWE^.hRuns^^[startRun];
  756.         if (pRuns^.first.runStart = pRuns^.second.runStart) then
  757.             begin
  758.                 err := _WERemoveRun(startRun, pWE);
  759.                 if (err <> noErr) then
  760.                     goto 1;
  761.                 startRun := startRun - 1;
  762.             end;
  763.  
  764. { merge the first and last runs if they have the same style index }
  765.         if (startRun >= 0) then
  766.             begin
  767.                 pRuns := @pWE^.hRuns^^[startRun];
  768.                 if (pRuns^.first.styleIndex = pRuns^.second.styleIndex) then
  769.                     begin
  770.                         err := _WERemoveRun(startRun + 1, pWE);
  771.                         if (err <> noErr) then
  772.                             goto 1;
  773.                     end;
  774.             end;
  775.  
  776. { clear result code }
  777.         err := noErr;
  778.  
  779. 1:
  780. { return result code }
  781.         _WERemoveRunRange := err;
  782.  
  783.     end;  { _WERemoveRunRange }
  784.  
  785.     procedure _WEBumpLineStart (lineIndex: LongInt;
  786.                                     deltaLineStart: LongInt;
  787.                                     pWE: WEPtr);
  788.  
  789. { add deltaLineStart to the lineStart field of all line records }
  790. { starting from lineIndex }
  791.  
  792.         var
  793.             pStart: LongIntPtr;
  794.             nLines: LongInt;
  795.     begin
  796.         pStart := @pWE^.hLines^^[lineIndex].lineStart;
  797.         nLines := pWE^.nLines;
  798.  
  799. { loop through the line array adjusting the lineStart fields }
  800.         while (lineIndex <= nLines) do
  801.             begin
  802.                 pStart^ := pStart^ + deltaLineStart;
  803.                 pStart := LongIntPtr(LongInt(pStart) + SizeOf(LineRec));
  804.                 lineIndex := lineIndex + 1;
  805.             end;
  806.     end;  { _WEBumpLineStart }
  807.  
  808.     function _WERemoveLineRange (rangeStart, rangeEnd: LongInt;
  809.                                     hWE: WEHandle): OSErr;
  810.  
  811. { the range of text between rangeStart and rangeEnd is being deleted }
  812. { update the line array accordingly }
  813. { the WE handle must be locked on entry }
  814.  
  815.         var
  816.             pWE: WEPtr;
  817.             startLine, endLine: LongInt;
  818.             err: OSErr;
  819.     begin
  820.         _WERemoveLineRange := noErr;
  821.         pWE := hWE^;
  822.  
  823. { remove all line records between rangeStart and rangeEnd }
  824.         startLine := WEOffsetToLine(rangeStart, hWE) + 1;
  825.         endLine := WEOffsetToLine(rangeEnd, hWE);
  826.         while (endLine >= startLine) do
  827.             begin
  828.                 err := _WERemoveLine(endLine, pWE);
  829.                 if (err <> noErr) then
  830.                     begin
  831.                         _WERemoveLineRange := err;
  832.                         Exit(_WERemoveLineRange);
  833.                     end;
  834.                 endLine := endLine - 1;
  835.             end;  { while }
  836.  
  837. { update the lineStart field of all the line records that follow }
  838.         _WEBumpLineStart(startLine, rangeStart - rangeEnd, pWE);
  839.  
  840.     end;  { _WERemoveLineRange }
  841.  
  842.     function _WEDeleteRange (rangeStart, rangeEnd: LongInt;
  843.                                     hWE: WEHandle): OSErr;
  844.  
  845. { used internally to delete a text range }
  846. { if saveNullStyle is TRUE, the first style in the range is saved in nullStyle }
  847. { the WE record is guaranteed to be already locked }
  848.  
  849.         label
  850.             0, 1;
  851.         var
  852.             pWE: WEPtr;
  853.             runInfo: WERunInfo;
  854.             oldTextLength, newTextLength: LongInt;
  855.             pText: LongInt;
  856.             err: OSErr;
  857.     begin
  858.         pWE := hWE^;
  859.  
  860. { do nothing if the specified range is empty }
  861.         if (rangeStart = rangeEnd) then
  862.             goto 0;
  863.  
  864. { save the first style in the specified range in nullStyle }
  865.         WEGetRunInfo(rangeStart, runInfo, hWE);
  866.         pWE^.nullStyle := runInfo.runAttrs;
  867.         BSET(pWE^.flags, weFUseNullStyle);
  868.  
  869. { special case: if we're deleting up to the end of the text, see whether }
  870. { there's an embedded object at the very end and remove it }
  871.         if (rangeEnd = pWE^.textLength) then
  872.             begin
  873.                 WEGetRunInfo(rangeEnd - 1, runInfo, hWE);
  874.                 if (runInfo.runAttrs.runStyle.tsObject <> kNullObject) then
  875.                     begin
  876.                         runInfo.runAttrs.runStyle.tsObject := kNullObject;
  877.                         err := _WESetStyleRange(rangeEnd - 1, rangeEnd, weDoObject, runInfo.runAttrs.runStyle, hWE);
  878.                         if (err <> noErr) then
  879.                             goto 1;
  880.                     end;
  881.             end;
  882.  
  883. { remove all line records between rangeStart and rangeEnd }
  884.         err := _WERemoveLineRange(rangeStart, rangeEnd, hWE);
  885.         if (err <> noErr) then
  886.             goto 1;
  887.  
  888. { remove all style runs between rangeStart and rangeEnd }
  889.         err := _WERemoveRunRange(rangeStart, rangeEnd, hWE);
  890.         if (err <> noErr) then
  891.             goto 1;
  892.  
  893. { calculate old and new text length }
  894.         oldTextLength := pWE^.textLength;
  895.         newTextLength := oldTextLength - (rangeEnd - rangeStart);
  896.  
  897. { move the end of the text backwards over the old selection range }
  898.         pText := LongInt(pWE^.hText^);
  899.         %_BlockMoveData(Ptr(pText + rangeEnd), Ptr(pText + rangeStart), oldTextLength - rangeEnd);
  900.  
  901. { compact the text handle }
  902.         err := %_SetHandleSize(pWE^.hText, newTextLength);
  903.         if (err <> noErr) then
  904.             goto 1;
  905.  
  906. { update textLength field }
  907.         pWE^.textLength := newTextLength;
  908.  
  909. 0:
  910. { clear result code }
  911.         err := noErr;
  912.  
  913. 1:
  914. { return result code }
  915.         _WEDeleteRange := err;
  916.  
  917.     end;  { _WEDeleteRange }
  918.  
  919.     function _WEInsertText (offset: LongInt;
  920.                                     textPtr: Ptr;
  921.                                     textLength: LongInt;
  922.                                     hWE: WEHandle): OSErr;
  923.  
  924. { this routine assumes that the WE record is already locked }
  925.  
  926.         label
  927.             0, 1;
  928.         var
  929.             pWE: WEPtr;
  930.             oldTextLength, newTextLength: LongInt;
  931.             pInsPoint: LongInt;
  932.             mode: Integer;
  933.             err: OSErr;
  934.     begin
  935.         pWE := hWE^;
  936.  
  937. { do nothing if textLength is zero or negative }
  938.         if (textLength <= 0) then
  939.             goto 0;
  940.  
  941. { calculate old and new length of text handle }
  942.         oldTextLength := pWE^.textLength;
  943.         newTextLength := oldTextLength + textLength;
  944.  
  945. { lengthen the raw text handle }
  946.         err := %_SetHandleSize(pWE^.hText, newTextLength);
  947.         if (err <> noErr) then
  948.             goto 1;
  949.  
  950. { calculate ptr to insertion point }
  951.         pInsPoint := LongInt(pWE^.hText^) + offset;
  952.  
  953. { make room for the new text }
  954.         %_BlockMoveData(Ptr(pInsPoint), Ptr(pInsPoint + textLength), oldTextLength - offset);
  955.  
  956. { insert new text at the insertion point }
  957.         %_BlockMoveData(textPtr, Ptr(pInsPoint), textLength);
  958.  
  959. { update the lineStart fields of all lines following the insertion point }
  960.         _WEBumpLineStart(WEOffsetToLine(offset, hWE) + 1, textLength, pWE);
  961.  
  962. { update the runStart fields of all style runs following the insertion point }
  963.         _WEBumpRunStart(_WEOffsetToRun(offset - 1, hWE) + 1, textLength, pWE);
  964.  
  965. { update various fields in the WE record }
  966.         pWE^.textLength := newTextLength;
  967.  
  968. { make sure the newly inserted text doesn't reference any embedded object }
  969.         pWE^.nullStyle.runStyle.tsObject := kNullObject;
  970.         mode := weDoObject;
  971.  
  972. { if there is a valid null style, apply it to the newly inserted text }
  973.         if BTST(pWE^.flags, weFUseNullStyle) then
  974.             mode := mode + (weDoAll + weDoReplaceFace);
  975.  
  976.         err := _WESetStyleRange(offset, offset + textLength, mode, pWE^.nullStyle.runStyle, hWE);
  977.         if (err <> noErr) then
  978.             goto 1;
  979.  
  980. 0:
  981. { clear result code }
  982.         err := noErr;
  983.  
  984. 1:
  985. { return result code }
  986.         _WEInsertText := err;
  987.  
  988.     end;  { _WEInsertText }
  989.  
  990. end.